import { Headers, useEntityManager, HttpError } from "@sker/core";
import { DependencyContainer } from "tsyringe";
import axios from 'axios'
import { WxAccount } from '@sker/entities'
import dayjs from "dayjs";
import { useData } from "./req";
declare const injector: DependencyContainer;
export interface AccessToken<E = number> {
    access_token: string;
    expires_in: E;
}
export interface GetUnlimitedQRCodeOptions {
    scene: string;
    page?: string;
    check_path?: boolean;
    width?: number;
    auto_color?: boolean;
    line_color?: { r: number, g: number, b: number };
    is_hyaline?: boolean;
    env_version?: 'release' | 'trial' | 'develop';
}
export interface SendMessageOptions {
    template_id: string;
    page?: string;
    touser: string;
    data: object;
    miniprogram_state: 'developer' | 'trial' | 'formal';
    lang: 'zh_CN' | 'en_US' | 'zh_HK' | 'zh_TW' | 'zh_CN'
}
export class Weapp {
    constructor(private id: string, private appid: string, private secret: string, private access_token: string, private exp_date: Date,) { }
    getAppid() {
        return this.appid;
    }
    async codeToSession(code: string) {
        const url = `https://api.weixin.qq.com/sns/jscode2session`;
        const params = {
            appid: this.appid,
            secret: this.secret,
            js_code: code,
            grant_type: 'authorization_code',
        };
        return axios
            .get(url, { params })
            .then((res) => res.data)
            .then((res) => {
                const { errcode, errmsg, ...other } = res;
                if (errcode) {
                    if (!(errcode === 0 || errmsg === 'ok')) {
                        throw new Error(errmsg);
                    }
                }
                return other;
            });
    }
    async getAccessToken(): Promise<string> {
        if (this.exp_date > new Date()) {
            return this.access_token
        }
        return await this.getStableAccessToken()
    }
    async saveAccessToken(res: AccessToken): Promise<string> {
        this.access_token = res.access_token;
        await useEntityManager(async m => {
            await m.update(WxAccount, this.id, {
                access_token: res.access_token, exp_date: dayjs().add(res.expires_in, 'seconds')
                    .format('YYYY-MM-DD HH:mm:ss')
            })
        })
        return res.access_token
    }
    async getStableAccessToken(force_refresh: boolean = false): Promise<string> {
        const url = `https://api.weixin.qq.com/cgi-bin/stable_token`
        const body = {
            grant_type: 'client_credential',
            appid: this.appid,
            secret: this.secret,
            force_refresh: force_refresh
        }
        return await axios.post(url, body).then(res => this.filterError(res.data)).then((res: AccessToken) => this.saveAccessToken(res))
    }
    async filterError(data: any) {
        const { errcode, errmsg, ...other } = data;
        if (errcode) {
            if (!(errcode === 0 || errmsg === 'ok')) {
                const test = /.*?rid\:(.*)/
                if (errcode === 40001) {
                    // refresh access token
                    throw new HttpError(`40001:invalid credential, access_token is invalid or not latest`)
                }
                const res = (errmsg as string).match(test)
                if (res && res.length > 0) {
                    const rid = res[1].trim()
                    if (rid) throw new HttpError(`${errcode}:${errmsg}:${rid}`)
                }
                throw new HttpError(`${errcode}:${errmsg}`)
            }
        }
        return other;
    }
    async getUnlimitedQRCode({ scene, page, env_version }: GetUnlimitedQRCodeOptions): Promise<any> {
        const access_token = await this.getAccessToken()
        const url = `https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=${access_token}`
        const body = {
            scene,
            page,
            check_path: false,
            env_version: env_version,
            width: 300
        }
        const res: any = await axios.post(url, body, {
            responseType: 'arraybuffer'
        }).then(res => res.data)
        if (res.errcode) {
            return res;
        }
        try {
            const demo = JSON.parse(Buffer.from(res).toString('utf-8'))
            if (demo.errcode) {
                return demo;
            }
        } catch (e) { }
        return res;
    }

    async sendMessage({ template_id, page, touser, data, miniprogram_state, lang }: SendMessageOptions) {
        const access_token = await this.getAccessToken()
        const url = `https://api.weixin.qq.com/cgi-bin/message/subscribe/send`
        const body = {
            access_token,
            template_id,
            page,
            touser,
            data,
            miniprogram_state,
            lang
        }
        return axios.post(url, body).then(res => res.data).then(this.filterError)
    }

    async getPhoneNumber(code: string) {
        const access_token = await this.getAccessToken()
        const url = `https://api.weixin.qq.com/wxa/business/getuserphonenumber`
        const body = {
            access_token,
            code
        }
        return axios.post(url, body).then(res => res.data).then(this.filterError)
    }
}
export interface WebappHandler<T> {
    (weapp: Weapp): Promise<T>;
}
export const useWeappConfig = async () => {
    const headers = injector.resolve(Headers)
    const body = useData()
    const appid = headers.appid || headers.APPID || body.appid || body.APPID;
    if (!appid) {
        return await useEntityManager(async m => {
            const res = await m.query(`select * from wx_account where is_default=$1`, [true])
            if (res.length > 0) return res[0]
            throw new HttpError(`没有找到`)
        })
    }
    // get the appid
    return await useEntityManager(async m => {
        const res = await m.query(`select * from wx_account where appid=$1`, [appid])
        if (res.length > 0) return res[0]
        throw new HttpError(`没有找到`)
    })
}
export const useWeapp = async () => {
    const wechatConfig = await useWeappConfig();
    if (!wechatConfig) throw new HttpError(`未找到小程序相关配置`)
    return new Weapp(wechatConfig.id, wechatConfig.appid, wechatConfig.secret, wechatConfig.access_token, wechatConfig.exp_date);
}
